/*
 * Copyright (c) 2012 Aalto University and University of Helsinki
 * MIT License
 * see LICENSE.txt for more information
 */
var treeIndex = {}; // contains all the tree nodes indexed by concept names.
var rest = rest_base_url; 

/* 
 * For legacy browsers that don't natively support Object.size().
 * @param {Object} obj
 */
Object.size = function (obj) {
  var size = 0, key;
  for (key in obj) {
    if (obj.hasOwnProperty(key)) {
      size += 1;
    }
  }
  return size;
};

/* 
 * A function for making asynchronous parent queries into the Onki Light REST API.
 * This function gets called by the concept page template.
 * @param {String} uri
 */
function queryConceptTree(uri) {
  if (uri === null) { return null; }
  var hierarchyurl = rest + vocab + '/broaderTransitive?uri=' + encodeURIComponent(uri) + '&lang=' + lang;
  $.when($.ajax({url: hierarchyurl})).done( 
    function (parents) { buildParentTree(uri, parents.broaderTransitive); }
  );
}

function getNode(uri) { return treeIndex[uri]; }

function setNode(node) { treeIndex[node.uri] = node; }

/* 
 * Initializes jsTree with the json formatted data for a concept and it's parents.
 * Also binds a window.location.href redirect to clicking a node.
 * @param {Object} tree
 */
function invokeParentTree(tree) {
  $('#hierarchy-tree').jstree(tree).on('loaded.jstree', function() {
    $('#hierarchy-tree').jstree('open_all');
    $('#hierarchy-tree').bind('select_node.jstree', function (event, data) {
      var node_uri = $('.jstree-clicked')[0].href;
      // If clicking a leafnode perform selection operation on the preflabel for easy copy pasting.
      if (data.rslt.obj.hasClass('jstree-leaf')) { return false; }
      window.location.href = node_uri;
    });
    // some hacking for the external concepts displayed eg. in afo @yso-concepts
    $('#hierarchy-tree li a:contains("@")').each(function(i) {
      var change = $('#hierarchy-tree li a:contains("@"):eq('+ i +')').text();
      var prefix = change.substring(change.indexOf('@'), change.indexOf(' '));
      change = change.replace(prefix, '');
      change = change.substring(2,change.length);
      change = '<ins class="jstree-icon"> </ins>' + change;
      change += ' <span id="jstree-vocablabel">' + prefix + '</span>'; 
      $('#hierarchy-tree li a:contains("@"):eq(' + i + ')').html(change);
    });
  });
}

/*
 * Creates a concept object from the data returned by a rest query.
 * @param
 * @param
 */
function createConceptObject(conceptUri, conceptData) {
  // redirect magic for uris originally directing to ONKI3 instead of ONKI Light
  var conUri = conceptUri.split('/');
  var redirectUri = document.location.href.split('/');
  // marks the concepts that need to be altered after jsTree has built the tree structure.
  var exVocStr = '';
  if (conUri[conUri.length-2] != redirectUri[redirectUri.length-4]) {
    exVocStr = '@' + conUri[conUri.length-2] + ' ';
  }
  redirectUri[redirectUri.length - 1] = conUri[conUri.length-1];
  redirectUri[redirectUri.length - 4] = conUri[conUri.length-2];
  redirectUri = redirectUri.join('/');
  newNode = { 
    data: { "title" : exVocStr + conceptData.prefLabel, "attr" : { "href" : redirectUri } },
    uri: conceptUri,
    parents: conceptData.broader,
    children: []
  };
  return newNode;
}

/*
 * For building a parent hierarchy tree from the leaf concept to the ontology/vocabulary root.
 * @param {String} uri
 * @param {Object} parentData
 */
function buildParentTree(uri, parentData) {	
  var loopIndex = 0, // for adding the last concept as a root if no better candidates have been found.
    currentNode,
    rootNode,
    leafNode = { 
      data: $('.prefLabel').text(), // taking the concept label from the view 
      uri: uri, 
      parents: [] 
    };
      
  if (parentData === undefined || parentData === null) { return; }
  if (Object.size(parentData) === 0) { invokeParentTree(getTreeConfiguration(leafNode)); return; }
  
  for(var conceptUri in parentData) {
    var previousNode = currentNode,
      branchHelper, 
      exactMatchFound;
    currentNode = createConceptObject(conceptUri, parentData[conceptUri]);
    /* if a node has the property topConceptOf set it as the root node. 
     * Or just setting the last node as a root if nothing else has been found 
     */
    if (!parentData[conceptUri].broader || ( loopIndex == Object.size(parentData)-1) && !rootNode) { 
      if (!rootNode) {  
        branchHelper = currentNode;
      }
      rootNode = currentNode; 
    }
    if (exactMatchFound) { // combining branches if we have met a exact match during the previous iteration.
      currentNode.children.push(branchHelper); 
      branchHelper = undefined;
      exactMatchFound = false;
    }
    // here we have iterated far enough to find the merging point of the trees.
    if (branchHelper && parentData[branchHelper.uri].exact === currentNode.uri) {
      exactMatchFound = true;
    } 
    setNode(currentNode);
    loopIndex++;
  }

  // Iterating over the nodes to make sure all concepts have their children set.
  appendChildrenToParents();
  invokeParentTree(getTreeConfiguration(rootNode));
}

/*
 * Iterates through the tree and fixes all the parents by adding references to their child concepts.
 */
function appendChildrenToParents() {
  for (var j in treeIndex) {
    var current = treeIndex[j];
    for (var index in current.parents) {
      var parentNode = getNode(current.parents[index]);
      if (parentNode && $.inArray(current, parentNode.children) === -1) {
        parentNode.children.push(current);
      }
    }
  }
}

/* 
 * Gives you the ONKI default jsTree configuration.
 * @param {Object} tree
 */
function getTreeConfiguration(tree) {
  if (tree === undefined) { return; }
  var jsonData = { json_data: { data: {} }, core: { animation: 0 }, themes: {} };
  jsonData.plugins = ['themes', 'json_data', 'ui', 'sort'];
  jsonData.themes = {
    theme: 'default',
    url: path_fix + 'lib/jsTree/default/style.css',
    icons: true,
    dots: true
  };
  jsonData.json_data = { data: tree };
  
  return jsonData;
}

/*
 * Iterates over the concepts children and generates a jsTree compatible object.
 * @param {String} json reply with the concept data. 
 * @param {String} uri
 */
function iterateOnlyChildren(json, uri) {
  var skosNarrower = 'http://www.w3.org/2004/02/skos/core#narrower',
      skosPrefLabel = 'http://www.w3.org/2004/02/skos/core#prefLabel';
  if (json[uri][skosNarrower] === undefined) { return null; }
  // json formatted child content for jsTree.
  var childData = [];
  for (var i = 0; i < json[uri][skosNarrower].length; i++) {
    var narrowerUri = json[uri][skosNarrower][i].value;
    var childNode, narrowerLabel;
    for (var j = 0; j < json[narrowerUri][skosPrefLabel].length; j++) {
      if (json[narrowerUri][skosPrefLabel][j].lang === lang) {
        narrowerLabel = json[narrowerUri][skosPrefLabel][j].value;
      }
      childNode = {data: narrowerLabel, id: narrowerLabel, uri: narrowerUri };
    }
    childData.push(childNode);
    setNode(childNode);
  }
  return childData;  
}

